﻿/*//-----------------------------------------------------------------
//  SpriteManager v0.64 (21-10-2012)
//  Copyright 2012 Brady Wright and Above and Beyond Software
//  All rights reserved
//-----------------------------------------------------------------
// A class to allow the drawing of multiple "quads" as part of a
// single aggregated mesh so as to achieve multiple, independently
// moving objects using a single draw call.
//-----------------------------------------------------------------
 
 
using UnityEngine;
using System.Collections;
 
 
//-----------------------------------------------------------------
// Describes a UV animation
//-----------------------------------------------------------------
//	NOTE: Currently, you should assign at least two frames to an
//	animation, or else you can expect problems!
//-----------------------------------------------------------------
public class UVAnimation
{
	protected Vector2[] frames;						// Array of UV coordinates (for quads) defining the frames of an animation
 
	// Animation state vars:
	protected int curFrame = 0;						// The current frame
	protected int stepDir = 1;						// The direction we're currently playing the animation (1=forwards (default), -1=backwards)
	protected int numLoops = 0;						// Number of times we've looped since last animation
 
	public string name;								// The name of the 
	public int loopCycles = 0;						// How many times to loop the animation (-1 loop infinitely)
	public bool loopReverse = false;				// Reverse the play direction when the end of the animation is reached? (if true, a loop iteration isn't counted until we return to the beginning)
	public float framerate;							// The rate in frames per second at which to play the animation
 
 
	// Resets all the animation state vars to ready the object
	// for playing anew:
	public void Reset()
	{
		curFrame = 0;
		stepDir = 1;
		numLoops = 0;
	}
 
	// Sets the stepDir to -1 and sets the current frame to the end
	// so that the animation plays in reverse
	public void PlayInReverse()
	{
		stepDir = -1;
		curFrame = frames.Length - 1;
	}
 
	// Stores the UV of the next frame in 'uv', returns false if
	// we've reached the end of the animation (this will never
	// happen if it is set to loop infinitely)
	public bool GetNextFrame(ref Vector2 uv)
	{
		// See if we can advance to the next frame:
		if((curFrame + stepDir) >= frames.Length || (curFrame + stepDir) < 0)
		{
			// See if we need to loop (if we're reversing, we don't loop until we get back to the beginning):
			if( stepDir>0 && loopReverse )
			{
				stepDir = -1;	// Reverse playback direction
				curFrame += stepDir;
 
				uv = frames[curFrame];
			}else
			{
				// See if we can loop:
				if (numLoops + 1 > loopCycles && loopCycles != -1)
					return false;
				else
				{	// Loop the animation:
					++numLoops;
 
					if (loopReverse)
					{
						stepDir *= -1;
						curFrame += stepDir;
					}
					else
						curFrame = 0;
 
					uv = frames[curFrame];
				}
			}
		}else
		{
			curFrame += stepDir;
			uv = frames[curFrame];
		}
 
		return true;
	}
 
	// Constructs an array of UV coordinates based upon the info
	// supplied.
	//
	// start	-	The UV of the lower-left corner of the first
	//				cell
	// cellSize	-	width and height, in UV space, of each cell
	// cols		-	Number of columns in the grid
	// rows		-	Number of rows in the grid
	// totalCells-	Total number of cells in the grid (left-to-right,
	//				top-to-bottom ordering is assumed, just like reading
	//				English).
	// fps		-	Framerate (frames per second)
	public Vector2[] BuildUVAnim(Vector2 start, Vector2 cellSize, int cols, int rows, int totalCells, float fps)
	{
		int cellCount = 0;
 
		frames = new Vector2[totalCells];
		framerate = fps;
 
		frames[0] = start;
 
		for(int row=0; row < rows; ++row)
		{
			for(int col=0; col<cols && cellCount < totalCells; ++col)
			{
				frames[cellCount].x = start.x + cellSize.x * ((float)col);
				frames[cellCount].y = start.y - cellSize.y * ((float)row);
 
				++cellCount;
			}
		}
 
		return frames;
	}
 
	// Assigns the specified array of UV coordinates to the
	// animation, replacing its current contents
	public void SetAnim(Vector2[] anim)
	{
		frames = anim;
	}
 
	// Appends the specified array of UV coordinates to the
	// existing animation
	public void AppendAnim(Vector2[] anim)
	{
		Vector2[] tempFrames = frames;
 
		frames = new Vector2[frames.Length + anim.Length];
		tempFrames.CopyTo(frames, 0);
		anim.CopyTo(frames, tempFrames.Length);
	}
}
 
 
 
 
//-----------------------------------------------------------------
// Holds a single mesh object which is composed of an arbitrary
// number of quads that all use the same material, allowing
// multiple, independently moving objects to be drawn on-screen
// while using only a single draw call.
//-----------------------------------------------------------------
public class SpriteManager : MonoBehaviour 
{
	// In which plane should we create the sprites?
	public enum SPRITE_PLANE
	{
		XY,
		XZ,
		YZ
	};
 
	// Which way to wind polygons?
	public enum WINDING_ORDER
	{
		CCW,		// Counter-clockwise
		CW			// Clockwise
	};
 
	public Material material;				// The material to use for the sprites
	public int allocBlockSize;				// How many sprites to allocate space for at a time. ex: if set to 10, 10 new sprite blocks will be allocated at a time. Once all of these are used, 10 more will be allocated, and so on...
	public SPRITE_PLANE plane;				// The plane in which to create the sprites
	public WINDING_ORDER winding=WINDING_ORDER.CCW;	// Which way to wind polygons
	public bool autoUpdateBounds = false;	// Automatically recalculate the bounds of the mesh when vertices change?
 
	protected ArrayList availableBlocks = new ArrayList(); // Array of references to sprites which are currently not in use
	protected bool vertsChanged = false;	// Have changes been made to the vertices of the mesh since the last frame?
	protected bool uvsChanged = false;		// Have changes been made to the UVs of the mesh since the last frame?
	protected bool colorsChanged = false;	// Have the colors changed?
	protected bool vertCountChanged = false;// Has the number of vertices changed?
	protected bool updateBounds = false;	// Update the mesh bounds?
	protected Sprite[] sprites;				// Array of all sprites (the offset of the vertices corresponding to each sprite should be found simply by taking the sprite's index * 4 (4 verts per sprite).
	protected ArrayList activeBlocks = new ArrayList();	// Array of references to all the currently active (non-empty) sprites
	protected ArrayList activeBillboards = new ArrayList(); // Array of references to all the *active* sprites which are to be rendered as billboards
	protected ArrayList playingAnimations = new ArrayList();// Array of references to all the sprites that are currently playing animation
	protected ArrayList spriteDrawOrder = new ArrayList();	// Array of indices of sprite objects stored in the order they are to be drawn (corresponding to the position of their vertex indices in the triIndices list)  Allows us to keep track of where a given Sprite is in the drawing order (triIndices)
	protected SpriteDrawLayerComparer drawOrderComparer = new SpriteDrawLayerComparer(); // Used to sort our draw order array
	protected float boundUpdateInterval;	// Interval, in seconds, to update the mesh bounds
 
 
	protected MeshFilter meshFilter;
	protected MeshRenderer meshRenderer;
	protected Mesh mesh;					// Reference to our mesh (contained in the MeshFilter)
 
	protected Vector3[] vertices;			// The vertices of our mesh
	protected int[] triIndices;				// Indices into the vertex array
	protected Vector2[] UVs;				// UV coordinates
	protected Color[] colors;				// Color values
	protected Vector3[] normals;			// Normals
 
	// Working vars:
	protected int i;
	protected Sprite tempSprite = null;
	protected float animTimeElapsed;
 
	//--------------------------------------------------------------
	// Utility functions:
	//--------------------------------------------------------------
 
	// Converts pixel-space values to UV-space scalar values
	// according to the currently assigned material.
	// NOTE: This is for converting widths and heights-not
	// coordinates (which have reversed Y-coordinates).
	// For coordinates, use PixelCoordToUVCoord()!
	public Vector2 PixelSpaceToUVSpace(Vector2 xy)
	{
		Texture t = material.GetTexture("_MainTex");
 
		return new Vector2(xy.x / ((float)t.width), xy.y / ((float)t.height));
	}
 
	// Converts pixel-space values to UV-space scalar values
	// according to the currently assigned material.
	// NOTE: This is for converting widths and heights-not
	// coordinates (which have reversed Y-coordinates).
	// For coordinates, use PixelCoordToUVCoord()!
	public Vector2 PixelSpaceToUVSpace(int x, int y)
	{
		return PixelSpaceToUVSpace(new Vector2((float)x, (float)y));
	}
 
	// Converts pixel coordinates to UV coordinates according to
	// the currently assigned material.
	// NOTE: This is for converting coordinates and will reverse
	// the Y component accordingly.  For converting widths and
	// heights, use PixelSpaceToUVSpace()!
	public Vector2 PixelCoordToUVCoord(Vector2 xy)
	{
		Vector2 p = PixelSpaceToUVSpace(xy);
		p.y = 1.0f - p.y;
		return p;
	}
 
	// Converts pixel coordinates to UV coordinates according to
	// the currently assigned material.
	// NOTE: This is for converting coordinates and will reverse
	// the Y component accordingly.  For converting widths and
	// heights, use PixelSpaceToUVSpace()!
	public Vector2 PixelCoordToUVCoord(int x, int y)
	{
		return PixelCoordToUVCoord(new Vector2((float)x, (float)y));
	}
 
	//--------------------------------------------------------------
	// End utility functions
	//--------------------------------------------------------------
 
	void Awake()
	{
		gameObject.AddComponent("MeshFilter");
		gameObject.AddComponent("MeshRenderer");
 
		meshFilter = (MeshFilter)GetComponent(typeof(MeshFilter));
		meshRenderer = (MeshRenderer)GetComponent(typeof(MeshRenderer));
 
		meshRenderer.renderer.material = material;
		mesh = meshFilter.mesh;
 
		// Create our first batch of sprites:
		EnlargeArrays(allocBlockSize);
 
		// Move the object to the origin so the objects drawn will not
		// be offset from the objects they are intended to represent.
		transform.position = Vector3.zero;
		transform.rotation = Quaternion.identity;
	}
 
	// Allocates initial arrays
	protected void InitArrays()
	{
		sprites = new Sprite[1];
		sprites[0] = new Sprite();
		vertices = new Vector3[4];
		UVs = new Vector2[4];
		colors = new Color[4];
		normals = new Vector3[4];
		triIndices = new int[6];
	}
 
	// Enlarges the sprite array by the specified count and also resizes
	// the UV and vertex arrays by the necessary corresponding amount.
	// Returns the index of the first newly allocated element
	// (ex: if the sprite array was already 10 elements long and is 
	// enlarged by 10 elements resulting in a total length of 20, 
	// EnlargeArrays() will return 10, indicating that element 10 is the 
	// first of the newly allocated elements.)
	protected int EnlargeArrays(int count)
	{
		int firstNewElement;
 
		if (sprites == null)
		{
			InitArrays();
			firstNewElement = 0;
			count = count - 1;	// Allocate one less since InitArrays already allocated one sprite for us
		}
		else
			firstNewElement = sprites.Length;
 
		// Resize sprite array:
		Sprite[] tempSprites = sprites;
		sprites = new Sprite[sprites.Length + count];
		tempSprites.CopyTo(sprites, 0);
 
		// Vertices:
		Vector3[] tempVerts = vertices;
		vertices = new Vector3[vertices.Length + count*4];
		tempVerts.CopyTo(vertices, 0);
 
		// UVs:
		Vector2[] tempUVs = UVs;
		UVs = new Vector2[UVs.Length + count*4];
		tempUVs.CopyTo(UVs, 0);
 
		// Colors:
		Color[] tempColors = colors;
		colors = new Color[colors.Length + count * 4];
		tempColors.CopyTo(colors, 0);
 
		// Normals:
		Vector3[] tempNormals = normals;
		normals = new Vector3[normals.Length + count*4];
		tempNormals.CopyTo(normals, 0);
 
		// Triangle indices:
		int[] tempTris = triIndices;
		triIndices = new int[triIndices.Length + count*6];
		tempTris.CopyTo(triIndices, 0);
 
		// Inform existing sprites of the new vertex and UV buffers:
		for (int i = 0; i < firstNewElement; ++i)
		{
			sprites[i].SetBuffers(vertices, UVs);
		}
 
		// Setup the newly-added sprites and Add them to the list of available 
		// sprite blocks. Also initialize the triangle indices while we're at it:
		for (int i = firstNewElement; i < sprites.Length; ++i)
		{
			// Create and setup sprite:
 
			sprites[i] = new Sprite();
			sprites[i].index = i;
			sprites[i].manager = this;
 
			sprites[i].SetBuffers(vertices, UVs);
 
			// Setup indices of the sprite's vertices in the vertex buffer:
			sprites[i].mv1 = i * 4 + 0;
			sprites[i].mv2 = i * 4 + 1;
			sprites[i].mv3 = i * 4 + 2;
			sprites[i].mv4 = i * 4 + 3;
 
			// Setup the indices of the sprite's UV entries in the UV buffer:
			sprites[i].uv1 = i * 4 + 0;
			sprites[i].uv2 = i * 4 + 1;
			sprites[i].uv3 = i * 4 + 2;
			sprites[i].uv4 = i * 4 + 3;
 
			// Setup the indices to the color values:
			sprites[i].cv1 = i * 4 + 0;
			sprites[i].cv2 = i * 4 + 1;
			sprites[i].cv3 = i * 4 + 2;
			sprites[i].cv4 = i * 4 + 3;
 
			// Setup the indices to the normal values:
			sprites[i].nv1 = i * 4 + 0;
			sprites[i].nv2 = i * 4 + 1;
			sprites[i].nv3 = i * 4 + 2;
			sprites[i].nv4 = i * 4 + 3;
 
			// Setup the default color:
			sprites[i].SetColor(Color.white);
 
			// Add as an available sprite:
			availableBlocks.Add(sprites[i]);
 
			// Init triangle indices:
			if(winding == WINDING_ORDER.CCW)
			{	// Counter-clockwise winding
				triIndices[i * 6 + 0] = i * 4 + 0;	//	0_ 2			0 ___ 3
				triIndices[i * 6 + 1] = i * 4 + 1;	//  | /		Verts:	 |	/|
				triIndices[i * 6 + 2] = i * 4 + 3;	// 1|/				1|/__|2
 
				triIndices[i * 6 + 3] = i * 4 + 3;	//	  3
				triIndices[i * 6 + 4] = i * 4 + 1;	//   /|
				triIndices[i * 6 + 5] = i * 4 + 2;	// 4/_|5
			}
			else
			{	// Clockwise winding
				triIndices[i * 6 + 0] = i * 4 + 0;	//	0_ 1			0 ___ 3
				triIndices[i * 6 + 1] = i * 4 + 3;	//  | /		Verts:	 |	/|
				triIndices[i * 6 + 2] = i * 4 + 1;	// 2|/				1|/__|2
 
				triIndices[i * 6 + 3] = i * 4 + 3;	//	  3
				triIndices[i * 6 + 4] = i * 4 + 2;	//   /|
				triIndices[i * 6 + 5] = i * 4 + 1;	// 5/_|4
			}
 
			// Add the index of this sprite to the draw order list
			spriteDrawOrder.Add(sprites[i]);
		}
 
		vertsChanged = true;
		uvsChanged = true;
		colorsChanged = true;
		vertCountChanged = true;
 
		return firstNewElement;
	}
 
	// Adds a sprite to the manager at the location and rotation of the client 
	// GameObject and with its transform.  Returns a reference to the new sprite
	// Width and height are in world space units
	// leftPixelX and bottomPixelY- the bottom-left position of the desired portion of the texture, in pixels
	// pixelWidth and pixelHeight - the dimensions of the desired portion of the texture, in pixels
	public Sprite AddSprite(GameObject client, float width, float height, int leftPixelX, int bottomPixelY, int pixelWidth, int pixelHeight, bool billboarded)
	{
		return AddSprite(client, width, height, PixelCoordToUVCoord(leftPixelX, bottomPixelY), PixelSpaceToUVSpace(pixelWidth, pixelHeight), Vector3.zero, billboarded);
	}
 
	// Same as the previous, but allows the use of a Vector3 offset for the sprite
	// Adds a sprite to the manager at the location and rotation of the client 
	// GameObject and with its transform.  Returns a reference to the new sprite
	// Width and height are in world space units
	// leftPixelX and bottomPixelY- the bottom-left position of the desired portion of the texture, in pixels
	// pixelWidth and pixelHeight - the dimensions of the desired portion of the texture, in pixels
	// offset - the sprite will be offseted by the specified Vector3 relative to the client transform
	public Sprite AddSprite(GameObject client, float width, float height, int leftPixelX, int bottomPixelY, int pixelWidth, int pixelHeight, Vector3 offset, bool billboarded)
	{
		return AddSprite(client, width, height, PixelCoordToUVCoord(leftPixelX, bottomPixelY), PixelSpaceToUVSpace(pixelWidth, pixelHeight), offset, billboarded);
	}
 
	// Adds a sprite to the manager at the location and rotation of the client 
	// GameObject and with its transform.  Returns a reference to the new sprite
	// Width and height are in world space units
	// lowerLeftUV - the UV coordinate for the upper-left corner
	// UVDimensions - the distance from lowerLeftUV to place the other UV coords
	// offset - the sprite will be offseted by the specified Vector3 relative to the client transform
	public Sprite AddSprite(GameObject client, float width, float height, Vector2 lowerLeftUV, Vector2 UVDimensions, Vector3 offset, bool billboarded)
	{
		int spriteIndex;
 
		// Get an available sprite:
		if (availableBlocks.Count < 1)
			EnlargeArrays(allocBlockSize);	// If we're out of available sprites, allocate some more:
 
		// Use a sprite from the list of available blocks:
		spriteIndex = ((Sprite)availableBlocks[0]).index;
		availableBlocks.RemoveAt(0);	// Now that we're using this one, remove it from the available list
 
		// Assign the new sprite:
		Sprite newSprite = sprites[spriteIndex];
		newSprite.client = client;
		newSprite.offset = offset;
		newSprite.lowerLeftUV = lowerLeftUV;
		newSprite.uvDimensions = UVDimensions;
 
 
		switch(plane)
		{
			case SPRITE_PLANE.XY:
				newSprite.SetSizeXY(width, height);
				break;
			case SPRITE_PLANE.XZ:
				newSprite.SetSizeXZ(width, height);
				break;
			case SPRITE_PLANE.YZ:
				newSprite.SetSizeYZ(width, height);
				break;
			default:
				newSprite.SetSizeXY(width, height);
				break;
		}
 
		// Save this to an active list now that it is in-use:
		if(billboarded)			
		{
			newSprite.billboarded = true;
			activeBillboards.Add(newSprite);
		}
		else
			activeBlocks.Add(newSprite);
 
		// Transform the sprite:
		newSprite.Transform();
 
		// Setup the UVs:
		UVs[newSprite.uv1] = lowerLeftUV + Vector2.up * UVDimensions.y;	 // Upper-left
		UVs[newSprite.uv2] = lowerLeftUV;								 // Lower-left
		UVs[newSprite.uv3] = lowerLeftUV + Vector2.right * UVDimensions.x;// Lower-right
		UVs[newSprite.uv4] = lowerLeftUV + UVDimensions;				// Upper-right
 
		// Calculate the normals
		Vector3 normal = CalculateNormal(newSprite);
 
		normals[newSprite.nv1] = normal;
		normals[newSprite.nv2] = normal;
		normals[newSprite.nv3] = normal;
		normals[newSprite.nv4] = normal;
 
		// Set our flags:
		vertsChanged = true;
		uvsChanged = true;
 
		return newSprite;
	}
 
	Vector3 CalculateNormal( Sprite sprite )
	{
 
		// Setup the normals
		// The normal of a triangle is found by calculating the cross product of two of its vectors.
		// We know both triangles of the generated mesh are coplanar, and as such have the same normal vector,
		// so we only have to calculate it once.
 
		// The winding order of the triangle must be followed, so we have to use triIndices to know the correct vertex order
 
		int[] indices = new int[3];
		int offset = spriteDrawOrder.IndexOf(sprite) * 6;
 
		if (offset < 0)
			return Vector3.zero;
 
		// Save our indices:
		indices[0] = triIndices[offset];
		indices[1] = triIndices[offset + 1];
		indices[2] = triIndices[offset + 2];
 
		Vector3 v1 = vertices[indices[1]] - vertices[indices[0]];
		Vector3 v2 = vertices[indices[2]] - vertices[indices[0]];
 
		Vector3 normal = Vector3.Cross(v1, v2);
 
		normal.Normalize();
 
		return normal;
 
	}
 
	public void SetBillboarded(Sprite sprite)
	{
		// Make sure the sprite isn't in the active list
		// or else it'll get handled twice:
		activeBlocks.Remove(sprite);
		activeBillboards.Add(sprite);
	}
 
	public void RemoveSprite(Sprite sprite)
	{
		sprite.SetSizeXY(0,0);
		sprite.v1 = Vector3.zero;
		sprite.v2 = Vector3.zero;
		sprite.v3 = Vector3.zero;
		sprite.v4 = Vector3.zero;
 
		vertices[sprite.mv1] = sprite.v1;
		vertices[sprite.mv2] = sprite.v2;
		vertices[sprite.mv3] = sprite.v3;
		vertices[sprite.mv4] = sprite.v4;
 
		// Remove the sprite from the billboarded list
		// since that list should only contain active
		// sprites:
		if (sprite.billboarded)
			activeBillboards.Remove(sprite);
		else
			activeBlocks.Remove(sprite);
 
		// Clean the sprite's settings:
		sprite.Clear();		
 
		availableBlocks.Add(sprite);
 
		vertsChanged = true;
	}
 
	public void HideSprite(Sprite sprite)
	{
		// Remove the sprite from the billboarded list
		// since that list should only contain sprites
		// we intend to transform:
		if (sprite.billboarded)
			activeBillboards.Remove(sprite);
		else
			activeBlocks.Remove(sprite);
 
		sprite.m_hidden___DoNotAccessExternally = true;
 
		vertices[sprite.mv1] = Vector3.zero;
		vertices[sprite.mv2] = Vector3.zero;
		vertices[sprite.mv3] = Vector3.zero;
		vertices[sprite.mv4] = Vector3.zero;
 
		vertsChanged = true;
	}
 
	public void ShowSprite(Sprite sprite)
	{
		// Only show the sprite if it has a client:
		if(sprite.client == null)
			return;
 
		if (!sprite.m_hidden___DoNotAccessExternally)
			return;
 
		sprite.m_hidden___DoNotAccessExternally = false;
 
		// Update the vertices:
		sprite.Transform();
 
		if (sprite.billboarded)
			activeBillboards.Add(sprite);
		else
			activeBlocks.Add(sprite);
 
		vertsChanged = true;
	}
 
 
	// Moves the specified sprite to the end of the drawing order
	public void MoveToFront(Sprite s)
	{
		int[] indices = new int[6];
		int offset = spriteDrawOrder.IndexOf(s) * 6;
 
		if (offset < 0)
			return;
 
		// Save our indices:
		indices[0] = triIndices[offset];
		indices[1] = triIndices[offset + 1];
		indices[2] = triIndices[offset + 2];
		indices[3] = triIndices[offset + 3];
		indices[4] = triIndices[offset + 4];
		indices[5] = triIndices[offset + 5];
 
		// Shift all indices from here forward down 6 slots (each sprite occupies 6 index slots):
		for (int i = offset; i < triIndices.Length - 6; i += 6)
		{
			triIndices[i] = triIndices[i+6];
			triIndices[i+1] = triIndices[i+7];
			triIndices[i+2] = triIndices[i+8];
			triIndices[i+3] = triIndices[i+9];
			triIndices[i+4] = triIndices[i+10];
			triIndices[i+5] = triIndices[i+11];
 
			spriteDrawOrder[i / 6] = spriteDrawOrder[i / 6 + 1];
		}
 
		// Place our desired index value at the end:
		triIndices[triIndices.Length - 6] = indices[0];
		triIndices[triIndices.Length - 5] = indices[1];
		triIndices[triIndices.Length - 4] = indices[2];
		triIndices[triIndices.Length - 3] = indices[3];
		triIndices[triIndices.Length - 2] = indices[4];
		triIndices[triIndices.Length - 1] = indices[5];
 
		// Update the sprite's index offset:
		spriteDrawOrder[spriteDrawOrder.Count - 1] = s.index;
 
		vertCountChanged = true;
	}
 
	// Moves the specified sprite to the start of the drawing order
	public void MoveToBack(Sprite s)
	{
		int[] indices = new int[6];
		int offset = spriteDrawOrder.IndexOf(s) * 6;
 
		if (offset < 0)
			return;
 
		// Save our indices:
		indices[0] = triIndices[offset];
		indices[1] = triIndices[offset + 1];
		indices[2] = triIndices[offset + 2];
		indices[3] = triIndices[offset + 3];
		indices[4] = triIndices[offset + 4];
		indices[5] = triIndices[offset + 5];
 
		// Shift all indices from here back up 6 slots (each sprite occupies 6 index slots):
		for(int i=offset; i>5; i-=6)
		{
			triIndices[i] = triIndices[i-6];
			triIndices[i+1] = triIndices[i-5];
			triIndices[i+2] = triIndices[i-4];
			triIndices[i+3] = triIndices[i-3];
			triIndices[i+4] = triIndices[i-2];
			triIndices[i+5] = triIndices[i-1];
 
			spriteDrawOrder[i / 6] = spriteDrawOrder[i / 6 - 1];
		}
 
		// Place our desired index value at the beginning:
		triIndices[0] = indices[0];
		triIndices[1] = indices[1];
		triIndices[2] = indices[2];
		triIndices[3] = indices[3];
		triIndices[4] = indices[4];
		triIndices[5] = indices[5];
 
		// Update the sprite's index offset:
		spriteDrawOrder[0] = s.index;
 
		vertCountChanged = true;
	}
 
	// Moves the first sprite in front of the second sprite by
	// placing it later in the draw order. If the sprite is already
	// in front of the reference sprite, nothing is changed:
	public void MoveInfrontOf(Sprite toMove, Sprite reference)
	{
		int[] indices = new int[6];
		int offset = spriteDrawOrder.IndexOf(toMove) * 6;
		int refOffset = spriteDrawOrder.IndexOf(reference) * 6;
 
		if (offset < 0)
			return;
 
		// Check to see if the sprite is already in front:
		if(offset > refOffset)
			return;
 
		// Save our indices:
		indices[0] = triIndices[offset];
		indices[1] = triIndices[offset + 1];
		indices[2] = triIndices[offset + 2];
		indices[3] = triIndices[offset + 3];
		indices[4] = triIndices[offset + 4];
		indices[5] = triIndices[offset + 5];
 
		// Shift all indices from here to the reference sprite down 6 slots (each sprite occupies 6 index slots):
		for (int i = offset; i < refOffset; i += 6)
		{
			triIndices[i] = triIndices[i+6];
			triIndices[i+1] = triIndices[i+7];
			triIndices[i+2] = triIndices[i+8];
			triIndices[i+3] = triIndices[i+9];
			triIndices[i+4] = triIndices[i+10];
			triIndices[i+5] = triIndices[i+11];
 
			spriteDrawOrder[i / 6] = spriteDrawOrder[i / 6 + 1];
		}
 
		// Place our desired index value at the destination:
		triIndices[refOffset] = indices[0];
		triIndices[refOffset+1] = indices[1];
		triIndices[refOffset+2] = indices[2];
		triIndices[refOffset+3] = indices[3];
		triIndices[refOffset+4] = indices[4];
		triIndices[refOffset+5] = indices[5];
 
		// Update the sprite's index offset:
		spriteDrawOrder[refOffset/6] = toMove.index;
 
		vertCountChanged = true;
	}
 
	// Moves the first sprite behind the second sprite by
	// placing it earlier in the draw order. If the sprite
	// is already behind, nothing is done:
	public void MoveBehind(Sprite toMove, Sprite reference)
	{
		int[] indices = new int[6];
		int offset = spriteDrawOrder.IndexOf(toMove) * 6;
		int refOffset = spriteDrawOrder.IndexOf(reference) * 6;
 
		if (offset < 0)
			return;
 
		// Check to see if the sprite is already behind:
		if(offset < refOffset)
			return;
 
		// Save our indices:
		indices[0] = triIndices[offset];
		indices[1] = triIndices[offset + 1];
		indices[2] = triIndices[offset + 2];
		indices[3] = triIndices[offset + 3];
		indices[4] = triIndices[offset + 4];
		indices[5] = triIndices[offset + 5];
 
		// Shift all indices from here to the reference sprite up 6 slots (each sprite occupies 6 index slots):
		for (int i = offset; i > refOffset; i -= 6)
		{
			triIndices[i] = triIndices[i-6];
			triIndices[i+1] = triIndices[i-5];
			triIndices[i+2] = triIndices[i-4];
			triIndices[i+3] = triIndices[i-3];
			triIndices[i+4] = triIndices[i-2];
			triIndices[i+5] = triIndices[i-1];
 
			spriteDrawOrder[i / 6] = spriteDrawOrder[i / 6 - 1];
		}
 
		// Place our desired index value at the destination:
		triIndices[refOffset] = indices[0];
		triIndices[refOffset+1] = indices[1];
		triIndices[refOffset+2] = indices[2];
		triIndices[refOffset+3] = indices[3];
		triIndices[refOffset+4] = indices[4];
		triIndices[refOffset+5] = indices[5];
 
		// Update the sprite's index offset:
		spriteDrawOrder[refOffset/6] = toMove.index;
 
		vertCountChanged = true;
	}
 
	// Rebuilds the drawing order based upon the drawing order buffer
	public void SortDrawingOrder()
	{
 
		Sprite s;
 
		spriteDrawOrder.Sort(drawOrderComparer);
 
		// Now reconstitute the triIndices in the order we want:
		if (winding == WINDING_ORDER.CCW)
		{
			for (int i = 0; i < spriteDrawOrder.Count; ++i)
			{
				s = (Sprite) spriteDrawOrder[i];
 
				// Counter-clockwise winding
				triIndices[i * 6 + 0] = s.mv1;		//	0_ 2			1 ___ 4
				triIndices[i * 6 + 1] = s.mv2;		//  | /		Verts:	 |	/|
				triIndices[i * 6 + 2] = s.mv4;		// 1|/				2|/__|3
 
				triIndices[i * 6 + 3] = s.mv4;		//	  3
				triIndices[i * 6 + 4] = s.mv2;		//   /|
				triIndices[i * 6 + 5] = s.mv3;		// 4/_|5
			}
		}
		else
		{
			for (int i = 0; i < spriteDrawOrder.Count; ++i)
			{
				s = (Sprite)spriteDrawOrder[i];
 
				// Clockwise winding
				triIndices[i * 6 + 0] = s.mv1;		//	0_ 1			0 ___ 3
				triIndices[i * 6 + 1] = s.mv4;		//  | /		Verts:	 |	/|
				triIndices[i * 6 + 2] = s.mv2;		// 2|/				1|/__|2
 
				triIndices[i * 6 + 3] = s.mv4;		//	  3
				triIndices[i * 6 + 4] = s.mv3;		//   /|
				triIndices[i * 6 + 5] = s.mv2;		// 5/_|4
			}
		}
 
		vertCountChanged = true;
	}
 
	public void AnimateSprite(Sprite s)
	{
		// Add this sprite to our playingAnimation list:
		playingAnimations.Add(s);
	}
 
	public void StopAnimation(Sprite s)
	{
		playingAnimations.Remove(s);
	}
 
	public Sprite GetSprite(int i)
	{
		if (i < sprites.Length)
			return sprites[i];
		else
			return null;
	}
 
	// Updates the vertices of a sprite based on the transform
	// of its client GameObject
	public void Transform(Sprite sprite)
	{
		sprite.Transform();
 
		vertsChanged = true;
	}
 
	// Updates the vertices of a sprite such that it is oriented
	// more or less toward the camera
	public void TransformBillboarded(Sprite sprite)
	{
		Vector3 pos = sprite.clientTransform.position;
		Transform t = Camera.main.transform;
 
		vertices[sprite.mv1] = pos + t.TransformDirection(sprite.v1);
		vertices[sprite.mv2] = pos + t.TransformDirection(sprite.v2);
		vertices[sprite.mv3] = pos + t.TransformDirection(sprite.v3);
		vertices[sprite.mv4] = pos + t.TransformDirection(sprite.v4);
 
		vertsChanged = true;
	}
 
	// Informs the SpriteManager that some vertices have changed position
	// and the mesh needs to be reconstructed accordingly
	public void UpdatePositions()
	{
		vertsChanged = true;
	}
 
	// Updates the UVs of the specified sprite and copies the new values
	// into the mesh object.
	public void UpdateUV(Sprite sprite)
	{
		UVs[sprite.uv1] = sprite.lowerLeftUV + Vector2.up * sprite.uvDimensions.y;	// Upper-left
		UVs[sprite.uv2] = sprite.lowerLeftUV;										// Lower-left
		UVs[sprite.uv3] = sprite.lowerLeftUV + Vector2.right * sprite.uvDimensions.x;// Lower-right
		UVs[sprite.uv4] = sprite.lowerLeftUV + sprite.uvDimensions;					// Upper-right
 
		uvsChanged = true;
	}
 
	// Updates the color values of the specified sprite and copies the
	// new values into the mesh object.
	public void UpdateColors(Sprite sprite)
	{
		colors[sprite.cv1] = sprite.color;
		colors[sprite.cv2] = sprite.color;
		colors[sprite.cv3] = sprite.color;
		colors[sprite.cv4] = sprite.color;
 
		colorsChanged = true;
	}
 
	// Instructs the manager to recalculate the bounds of the mesh
	public void UpdateBounds()
	{
		updateBounds = true;
	}
 
	// Schedules a recalculation of the mesh bounds to occur at a
	// regular interval (given in seconds):
	public void ScheduleBoundsUpdate(float seconds)
	{
		boundUpdateInterval = seconds;
		InvokeRepeating("UpdateBounds", seconds, seconds);
	}
 
	// Cancels any previously scheduled bounds recalculations:
	public void CancelBoundsUpdate()
	{
		CancelInvoke("UpdateBounds");
	}
 
	// Use this for initialization
	void Start () 
	{
 
	}
 
	// LateUpdate is called once per frame
	virtual public void LateUpdate () 
	{
		// See if we have any active animations:
		if(playingAnimations.Count > 0)
		{
			animTimeElapsed = Time.deltaTime;
 
			for(i=0; i<playingAnimations.Count; ++i)
			{
				tempSprite = (Sprite)playingAnimations[i];
 
				// Step the animation, and if it has finished
				// playing, remove it from the playing list:
				if (!tempSprite.StepAnim(animTimeElapsed))
					playingAnimations.Remove(tempSprite);
			}
 
			uvsChanged = true;
		}
 
		// Were changes made to the mesh since last time?
		if (vertCountChanged)
		{
			vertCountChanged = false;
			colorsChanged = false;
			vertsChanged = false;
			uvsChanged = false;
			updateBounds = false;
 
			mesh.Clear();
			mesh.vertices = vertices;
			mesh.uv = UVs;
			mesh.colors = colors;
			mesh.normals = normals;
			mesh.triangles = triIndices;
		}
		else
		{
			if (vertsChanged)
			{
				vertsChanged = false;
 
				if (autoUpdateBounds)
					updateBounds = true;
 
				mesh.vertices = vertices;
				mesh.normals = normals;
			}
 
			if (updateBounds)
			{
				mesh.RecalculateBounds();
				updateBounds = false;
			}
 
			if (colorsChanged)
			{
				colorsChanged = false;
				mesh.colors = colors;
			}
 
			if (uvsChanged)
			{
				uvsChanged = false;
				mesh.uv = UVs;
			}
		}
	}
}

You can also change the Reset() function on the SpriteManager to make sure the first frame is played in the first loop: 
public void Reset()
{
	curFrame = -1; //this is the change
	stepDir = 1;
	numLoops = 0;
}
LinkedSpriteManager.cs
//-----------------------------------------------------------------
//  LinkedSpriteManager v0.64 (21-10-2012)
//  Copyright 2012 Brady Wright and Above and Beyond Software
//  All rights reserved
//-----------------------------------------------------------------
// A class to allow the drawing of multiple "quads" as part of a
// single aggregated mesh so as to achieve multiple, independently
// moving objects using a single draw call.
//-----------------------------------------------------------------
 
 
using UnityEngine;
using System.Collections;
 
// A variation on the SpriteManager that automatically links all
// translations and rotations of the client GameObjects to the
// associated sprite - meaning the client need not worry about
// micromanaging all transformations:
public class LinkedSpriteManager : SpriteManager 
{
	Transform t;
	Vector3 pos;
	Sprite s;
 
 
	// Use this for initialization
	void Start () 
	{
 
	}
 
	// Transforms all sprites by their associated GameObject's
	// transforms:
	void TransformSprites()
	{
		for(int i=0; i<activeBlocks.Count; ++i)
		{
			((Sprite)activeBlocks[i]).Transform();
		}
 
		// Handle any billboarded sprites:
		if(activeBillboards.Count > 0)
		{
			t = Camera.main.transform;
 
			for(int i=0; i<activeBillboards.Count; ++i)
			{
				s = (Sprite)activeBillboards[i];
				pos = s.clientTransform.position;
 
				vertices[s.mv1] = pos + t.TransformDirection(s.v1);
				vertices[s.mv2] = pos + t.TransformDirection(s.v2);
				vertices[s.mv3] = pos + t.TransformDirection(s.v3);
				vertices[s.mv4] = pos + t.TransformDirection(s.v4);
			}
		}
	}
 
	// LateUpdate is called once per frame
	new void LateUpdate() 
	{
		// Transform all sprites according to their
		// client GameObject's transforms:
		TransformSprites();
 
		// Copy over the changes:
		mesh.vertices = vertices;
 
		// See if we have any active animations:
		if (playingAnimations.Count > 0)
		{
			animTimeElapsed = Time.deltaTime;
 
			for (i = 0; i < playingAnimations.Count; ++i)
			{
				tempSprite = (Sprite)playingAnimations[i];
 
				// Step the animation, and if it has finished
				// playing, remove it from the playing list:
				if (!tempSprite.StepAnim(animTimeElapsed))
					playingAnimations.Remove(tempSprite);
			}
 
			uvsChanged = true;
		}
 
		if (vertCountChanged)
		{
			mesh.uv = UVs;
			mesh.colors = colors;
			mesh.normals = normals;
			mesh.triangles = triIndices;
 
			vertCountChanged = false;
			uvsChanged = false;
			colorsChanged = false;
		}
		else
		{
			if (uvsChanged)
			{
				mesh.uv = UVs;
				uvsChanged = false;
			}
 
			if (colorsChanged)
			{
				colorsChanged = false;
 
				mesh.colors = colors;
			}
 
			// Explicitly recalculate bounds since
			// we didn't assign new triangles (which
			// implicitly recalculates bounds)
			if (updateBounds || autoUpdateBounds)
			{
				mesh.RecalculateBounds();
				updateBounds = false;
			}
		}
	}
}
Sprite.cs
//-----------------------------------------------------------------
//  Sprite (part of SpriteManager) v0.64 (21-10-2012)
//  Copyright 2012 Brady Wright and Above and Beyond Software
//  All rights reserved
//-----------------------------------------------------------------
// A class to allow the drawing of multiple "quads" as part of a
// single aggregated mesh so as to achieve multiple, independently
// moving objects using a single draw call.
//-----------------------------------------------------------------
 
 
using UnityEngine;
using System.Collections;
 
//-----------------------------------------------------------------
// Describes a sprite
//-----------------------------------------------------------------
public class Sprite
{
	protected float m_width;					// Width and Height of the sprite in worldspace units
	protected float m_height;
	protected Vector2 m_lowerLeftUV;			// UV coordinate for the upper-left corner of the sprite
	protected Vector2 m_UVDimensions;			// Distance from the upper-left UV to place the other UVs
	protected GameObject m_client;				// Reference to the client GameObject
	protected SpriteManager m_manager;			// Reference to the sprite manager in which this sprite resides
	protected bool m_billboarded = false;		// Is the sprite to be billboarded?
	public bool m_hidden___DoNotAccessExternally = false;	// Indicates whether this sprite is currently hidden (has to be public because C# has no "friend" feature, just don't access directly from outside)
 
	protected Vector3[] meshVerts;				// Pointer to the array of vertices in the mesh
	protected Vector2[] UVs;					// Pointer to the array of UVs in the mesh
	protected Vector3[] normals;				// Pointer to the array of normals in the mesh
 
	public Transform clientTransform;			// Transform of the client GameObject
	public Vector3 offset = new Vector3();		// Offset of sprite from center of client GameObject
	public Color color;							// The color to be used by all four vertices
 
	public int index;							// Index of this sprite in its SpriteManager's list
	public int drawLayer;						// The draw layer indicating the order in which this sprite should be rendered relative to other sprites
 
	public Vector3 v1 = new Vector3();			// The sprite's vertices in local space
	public Vector3 v2 = new Vector3();
	public Vector3 v3 = new Vector3();
	public Vector3 v4 = new Vector3();
 
	public int mv1;							// Indices of the associated vertices in the actual mesh (this just provides a quicker way for the SpriteManager to get straight to the right vertices in the vertex array)
	public int mv2;
	public int mv3;
	public int mv4;
 
	public int uv1;							// Indices of the associated UVs in the mesh
	public int uv2;
	public int uv3;
	public int uv4;
 
	public int cv1;							// Indices of the associated color values in the mesh
	public int cv2;
	public int cv3;
	public int cv4;
 
	public int nv1;							// Indices of the associated normal values in the mesh
	public int nv2;
	public int nv3;
	public int nv4;
 
	// Animation-related vars and types:
	public delegate void AnimCompleteDelegate();		// Definition of delegate to be called upon animation completion
 
	protected ArrayList animations = new ArrayList();	// Array of available animations
	protected UVAnimation curAnim = null;				// The current animation
	protected AnimCompleteDelegate animCompleteDelegate = null;	// Delegate to be called upon animation completion
	protected float timeSinceLastFrame = 0;				// The total time since our last animation frame change
	protected float timeBetweenAnimFrames;				// The amount of time we want to pass before moving to the next frame of animation
	protected int framesToAdvance;						// (working) The number of animation frames to advance given the time elapsed
 
	~Sprite()
	{
	}
 
 
	public Sprite()
	{
		m_width = 0;
		m_height = 0;
		m_client = null;
		m_manager = null;
		clientTransform = null;
		index = 0;
		drawLayer = 0;
		color = Color.white;
 
		offset = Vector3.zero;
	}
 
	public SpriteManager manager
	{
		get { return m_manager; }
		set { m_manager = value; }
	}
 
	public GameObject client
	{
		get { return m_client; }
		set
		{
			m_client = value;
			if (m_client != null)
				clientTransform = m_client.transform;
			else
				clientTransform = null;
		}
	}
 
	public Vector2 lowerLeftUV
	{
		get { return m_lowerLeftUV; }
		set
		{
			m_lowerLeftUV = value;
			m_manager.UpdateUV(this);
		}
	}
 
	public Vector2 uvDimensions
	{
		get { return m_UVDimensions; }
		set
		{
			m_UVDimensions = value;
			m_manager.UpdateUV(this);
		}
	}
 
	public float width
	{
		get { return m_width; }
	}
 
	public float height
	{
		get { return m_height; }
	}
 
	public bool billboarded
	{
		get { return m_billboarded; }
		set
		{
			m_billboarded = value;
		}
	}
 
	public bool hidden
	{
		get { return m_hidden___DoNotAccessExternally; }
		set
		{
			// No need to do anything if we're
			// already in this state:
			if (value == m_hidden___DoNotAccessExternally)
				return;
 
			if (value)
				m_manager.HideSprite(this);
			else
				m_manager.ShowSprite(this);
		}
	}
 
	// Resets all sprite values to defaults for reuse:
	public void Clear()
	{
		client = null;
		billboarded = false;
		hidden = false;
		SetColor(Color.white);
		offset = Vector3.zero;
 
		PauseAnim();
		animations.Clear();
		curAnim = null;
		animCompleteDelegate = null;
	}
 
	// Does the same as assigning the drawLayer value, except that
	// SortDrawingOrder() is called automatically.
	// The draw layer indicates the order in which this sprite should be 
	// rendered relative to other sprites. Higher values result in a later
	// drawing order relative to sprites with lower values:
	public void SetDrawLayer(int v)
	{
		drawLayer = v;
		m_manager.SortDrawingOrder();
	}
 
	// Sets the physical dimensions of the sprite in the XY plane:
	public void SetSizeXY(float width, float height)
	{
		m_width = width;
		m_height = height;
		v1 = offset + new Vector3(-m_width / 2, m_height / 2, 0);	// Upper-left
		v2 = offset + new Vector3(-m_width / 2, -m_height / 2, 0);	// Lower-left
		v3 = offset + new Vector3(m_width / 2, -m_height / 2, 0);	// Lower-right
		v4 = offset + new Vector3(m_width / 2, m_height / 2, 0);	// Upper-right
 
		Transform();
	}
 
	// Sets the physical dimensions of the sprite in the XZ plane:
	public void SetSizeXZ(float width, float height)
	{
		m_width = width;
		m_height = height;
		v1 = offset + new Vector3(-m_width / 2, 0, m_height / 2);	// Upper-left
		v2 = offset + new Vector3(-m_width / 2, 0, -m_height / 2);	// Lower-left
		v3 = offset + new Vector3(m_width / 2, 0, -m_height / 2);	// Lower-right
		v4 = offset + new Vector3(m_width / 2, 0, m_height / 2);	// Upper-right
 
		Transform();
	}
 
	// Sets the physical dimensions of the sprite in the YZ plane:
	public void SetSizeYZ(float width, float height)
	{
		m_width = width;
		m_height = height;
		v1 = offset + new Vector3(0, m_height / 2, -m_width / 2);	// Upper-left
		v2 = offset + new Vector3(0, -m_height / 2, -m_width / 2);	// Lower-left
		v3 = offset + new Vector3(0, -m_height / 2, m_width / 2);	// Lower-right
		v4 = offset + new Vector3(0, m_height / 2, m_width / 2);	// Upper-right
 
		Transform();
	}
 
	// Sets the vertex and UV buffers
	public void SetBuffers(Vector3[] v, Vector2[] uv)
	{
		meshVerts = v;
		UVs = uv;
	}
 
	// Applies the transform of the client GameObject and stores
	// the results in the associated vertices of the overall mesh:
	public void Transform()
	{
		meshVerts[mv1] = clientTransform.TransformPoint(v1);
		meshVerts[mv2] = clientTransform.TransformPoint(v2);
		meshVerts[mv3] = clientTransform.TransformPoint(v3);
		meshVerts[mv4] = clientTransform.TransformPoint(v4);
 
		m_manager.UpdatePositions();
	}
 
	// Applies the transform of the client GameObject and stores
	// the results in the associated vertices of the overall mesh:
	public void TransformBillboarded(Transform t)
	{
		Vector3 pos = clientTransform.position;
 
		meshVerts[mv1] = pos + t.InverseTransformDirection(v1);
		meshVerts[mv2] = pos + t.InverseTransformDirection(v2);
		meshVerts[mv3] = pos + t.InverseTransformDirection(v3);
		meshVerts[mv4] = pos + t.InverseTransformDirection(v4);
 
		m_manager.UpdatePositions();
	}
 
	// Sets the specified color and automatically notifies the
	// SpriteManager to update the colors:
	public void SetColor(Color c)
	{
		color = c;
		m_manager.UpdateColors(this);
	}
 
	//-----------------------------------------------------------------
	// Animation-related routines:
	//-----------------------------------------------------------------
 
	// Sets the delegate to be called upon animation completion:
	public void SetAnimCompleteDelegate(AnimCompleteDelegate del)
	{
		animCompleteDelegate = del;
	}
 
	// Adds an animation to the sprite
	public void AddAnimation(UVAnimation anim)
	{
		animations.Add(anim);
	}
 
	// Steps to the next frame of sprite animation
	public bool StepAnim(float time)
	{
		if (curAnim == null)
			return false;
 
		timeSinceLastFrame += time;
 
		framesToAdvance = (int) (timeSinceLastFrame / timeBetweenAnimFrames);
 
		// If there's nothing to do, return:
		if (framesToAdvance < 1)
			return true;
 
		while(framesToAdvance > 0)
		{
			if (curAnim.GetNextFrame(ref m_lowerLeftUV))
				--framesToAdvance;
			else
			{
				// We reached the end of our animation
				if (animCompleteDelegate != null)
					animCompleteDelegate();
 
				m_manager.UpdateUV(this);
 
				return false;
			}
		}
 
		m_manager.UpdateUV(this);
		timeSinceLastFrame = 0;
 
		return true;
	}
 
	// Starts playing the specified animation
	// (Note: this doesn't resume from a pause,
	// it completely restarts the animation. To
	// unpause, use UnpauseAnim):
	public void PlayAnim(UVAnimation anim)
	{
		// First stop any currently playing animation:
		m_manager.StopAnimation(this);
 
		curAnim = anim;
		curAnim.Reset();
		timeBetweenAnimFrames = 1f / anim.framerate;
		timeSinceLastFrame = timeBetweenAnimFrames;
		StepAnim(0);
 
		m_manager.AnimateSprite(this);
	}
 
	// Starts playing the specified animation:
	public void PlayAnim(string name)
	{
		for (int i = 0; i < animations.Count; ++i)
		{
			if (((UVAnimation)animations[i]).name == name)
				PlayAnim((UVAnimation)animations[i]);
		}
	}
 
	// Like PlayAnim but plays in reverse:
	public void PlayAnimInReverse(UVAnimation anim)
	{
		// First stop any currently playing animation:
		m_manager.StopAnimation(this);
 
		curAnim = anim;
		curAnim.Reset();
		curAnim.PlayInReverse();
		timeBetweenAnimFrames = 1f / anim.framerate;
		timeSinceLastFrame = timeBetweenAnimFrames;
		StepAnim(0);
 
		m_manager.AnimateSprite(this);
	}
 
	// Starts playing the specified animation in reverse:
	public void PlayAnimInReverse(string name)
	{
		for (int i = 0; i < animations.Count; ++i)
		{
			if (((UVAnimation)animations[i]).name == name)
			{
				((UVAnimation)animations[i]).PlayInReverse();
				PlayAnimInReverse((UVAnimation)animations[i]);
			}
		}
	}
 
	// Pauses the currently-playing animation:
	public void PauseAnim()
	{
		m_manager.StopAnimation(this);
	}
 
	// Unpauses the currently-playing animation:
	public void UnpauseAnim()
	{
		if (curAnim == null) return;
 
		m_manager.AnimateSprite(this);
	}
}
 
 
// Compares drawing layers of sprites
public class SpriteDrawLayerComparer : IComparer
{
	static Sprite s1;
	static Sprite s2;
 
	int IComparer.Compare(object a, object b)
	{
		s1 = (Sprite)a;
		s2 = (Sprite)b;
 
		if (s1.drawLayer > s2.drawLayer)
			return 1;
		else if (s1.drawLayer < s2.drawLayer)
			return -1;
		else
			return 0;
	}
}*/